/*
 * AGenApp.java
 */
package agen;

import agen.cruce.Cruce;
import agen.cruce.MetodoCruceCombinado;
import agen.mutacion.Mutacion;
import agen.optimalidad.Aptitud;
import agen.optimalidad.Optimo;
import agen.seleccion.MetodoSeleccionCombinado;
import agen.seleccion.Seleccion;
import agen.util.AHPTableModel;
import agen.util.Arreglos;
import agen.util.CorrerThreaded;
import agen.util.IndividuoTableModel;
import agen.util.exception.MetodoInexistente;
import agen.util.exception.PorcentajesInconsistentes;
import agen.util.graficos.ArmarDataSets;
import agen.util.graficos.XYChart;
import java.util.Arrays;
import java.util.Random;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import org.jblas.DoubleMatrix;
import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

/**
 * The main class of the application.
 */
public class AGenApp extends SingleFrameApplication {
    /* Vector de longitud poblacion * eltosIndividuo que contiene a la poblacion
     * implementada con bytes donde cada byte (limitado entre 0 y 16) referencia
     * a un elemento en el array de prioridades*/
    
    public static byte[] poblacion;
	public static byte[] poblacionModificada;
	public static byte[] poblacionAux; /* para probar la diferencia de eficiencia 
										* que hay entre instanciar nuevas poblaciones 
										* y manejar punteros unicamnete*/
    public static int batchNumCorridas; /*Almacena sin modificarse el numero de corridas, 
                                para no tener que ir hasta la interface*/
    public static int cortarEnIteracion = -1;
    public static boolean pause = false;
    public static int poblacionInicial = 100;
	/* vector de prioridades*/
    public final static double[] prioridades = {1 / 9.0, 1 / 8.0, 1 / 7.0, 1 / 6.0,
        1 / 5.0, 1 / 4.0, 1 / 3.0, 1 / 2.0, 2, 3, 4, 5, 6, 7, 8, 9};
    

    private static String armarStringIndividuo(byte[] individuoOp) {
        String ind = "";
        double prio;
        for (int i = 0; i < individuoOp.length; i++) {
            ind += "<";
            prio = prioridades[individuoOp[i]];
            ind += prio > 1 ? String.valueOf((int)prio) : "1 / " + Math.round(1 / prio);
            ind +=">";
        }
        return ind;
    }

    /*corriendo, independiente de pausa, indica si el algoritmo esta corriendo.*/
    public static boolean corriendo = false;
    /*TODO: IMPLEMENTAR CUADNO SE PUEDA, en cada generacion almacenar las aptitudes
     * aca para graficarlas. */
    public static double[][] historialAptitudes;
    public static byte[][] historialpoblaciones;
    public static int[][] historialAptitudesOrdenadas;

    private static AGenView interfaz;

    public static AGenView getInterfaz() {
        return interfaz;
    }
    private static CorrerThreaded correrThead;

    public static CorrerThreaded getCorrerThead() {
        return correrThead;
    }
    /**
     * At startup create and show the main frame of the application.
     */
    @Override
    protected void startup() {
        interfaz = new AGenView(this);
        show(interfaz);
    }

    /**
     * This method is to initialize the specified window by injecting resources.
     * Windows shown in our application come fully initialized from the GUI
     * builder, so this additional configuration is not needed.
     */
    @Override
    protected void configureWindow(java.awt.Window root) {
    }

    /**
     * A convenient static getter for the application instance.
     * @return the instance of AGenApp
     */
    public static AGenApp getApplication() {
        return Application.getInstance(AGenApp.class);
    }

    /**
     * Main method launching the application.
     */
    public static void main(String[] args) {
        launch(AGenApp.class, args);
    }

    /**
     * Implementa un Array unidimencional con cantIndividuos individuos.
     * asi: si un indiviuo tiene tres elementos una poblacion de 2 individuos es
     * un byte[2 * 3] => byte[6] donde byte[0], byte[1] y byte[2] seran el
     * primer individuo.
     * Esto ahorra memoria y procesamiento.
     * @param cantIndividuos
     * @param iModel
     */
    public static void generarPoblacionInicial(int cantIndividuos,
            int eltosIndividuo) {
        /* byte es la representacion mas chica que me
         * permita tener 17 elementos: 1/9 , 1/8, 1/7, .. , 2, .. , 9
         * :)*/
        poblacion = new byte[cantIndividuos * eltosIndividuo];
        Random r1 = new Random();
        System.out.println(System.currentTimeMillis());
        for (int i = poblacion.length - 1; i >= 0; i--) {
            poblacion[i] = (byte) r1.nextInt(16);
        }

    /*
    System.out.println("Probando Jampack");
    aptitudes = Aptitud.calcularAptitudJampack(poblacion,
    prioridades, ubicaciones, matriz, null);
    millis =  System.currentTimeMillis() - millis;
    System.out.println("Fin Jampack, tardo: " + millis);

    printAptitudes(20, aptitudes);

    millis = System.currentTimeMillis();
    System.out.println("Probando MTJ");
    aptitudes = Aptitud.calcularAptitudMTJ(poblacion, prioridades,
    ubicaciones, matriz);
    millis =  System.currentTimeMillis() - millis;
    System.out.println("Fin MTJ tardo: " + millis);

    printAptitudes(20, aptitudes);
     */

    //printAptitudes(20, aptitudes);
    }

    private void printAptitudes(int cantidad, double[] aptitudes) {
        while (cantidad-- > 0) {
            System.out.print(aptitudes[cantidad] + " # ");
        }
        System.out.println("");
    }

    /**
     * devuelve prioridades obtenidas aleatoriamente.
     * @param cantNumeros
     * @return
     */
    public static float calcularPrioridadAleatoria() {
        Random r1 = new Random();
        boolean decimal = (r1.nextInt(2) == 1) ? false : true;
        int prioridad = r1.nextInt(9) + 1;
        while (prioridad == 1){
            prioridad = r1.nextInt(9) + 1;
        }
        float salida = (decimal) ? 1 / (float) prioridad : prioridad;
        return salida;
    }

    /**
     * Desde aquí se administra la ejecución del algoritmo genetico.
     * TODO: completar documentacion
     * @param interfaz
     */
    synchronized public static void correr(double[][] matrizMulticriterio,
            int[][] ubicaciones, int porcentajeSeleccion,int porcentajeCruce,
            int porcentajeMutacion, int porcentajeElitista, int porcentajeRanking,
            int porcentajeRuleta, int porcentajeBinomial, int porcentajePunto,
            int porcentajeMultiPunto, int[] multiPunto, int metodoMutacion,
            double maxPMutacion, double MinPMutacion, boolean emitirLog,
            boolean correrBatch, int numCorridasBatch) {
        if (!correrBatch) {
            numCorridasBatch = 1;
        }
        batchNumCorridas = numCorridasBatch;
        while (numCorridasBatch > 0){
         if (!corriendo){
            if (correrBatch) {
                cambiarValoresMatrizCriterios();
            }
            corriendo = true;
            historialAptitudes = new double[100][];
            historialpoblaciones = new byte[100][];
            historialAptitudesOrdenadas = new int[100][];
            /*matriz de prioridades con los huecos*/
            DoubleMatrix matriz = new DoubleMatrix(matrizMulticriterio);

            generarPoblacionInicial(poblacionInicial,
                    ubicaciones.length);
            poblacionModificada = new byte[poblacion.length];

            correrThead = new CorrerThreaded(matriz, ubicaciones,
                    porcentajeSeleccion, porcentajeCruce, porcentajeMutacion,
                    porcentajeElitista, porcentajeRanking, porcentajeRuleta,
                    porcentajeBinomial, porcentajePunto, porcentajeMultiPunto,
                    multiPunto, metodoMutacion, maxPMutacion, MinPMutacion,
                    poblacion, poblacionModificada, cortarEnIteracion, emitirLog,
                    correrBatch,numCorridasBatch);
            correrThead.start();
            if (correrBatch){
                try {
                    correrThead.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            numCorridasBatch--;
            } else {

                synchronized (correrThead) {
                    correrThead.notify();
                }

            }
        }

    }

  private static void cambiarValoresMatrizCriterios(){
        AHPTableModel model = (AHPTableModel) interfaz.getJtbMultiCriterioModel();
        model.completandoConRandom = true;
        for (int i = 0; i < model.getRowCount(); i++) {
            for (int j = i + 2; j < model.getColumnCount(); j++) {
                if (!model.getValueAt(i, j).equals("¿?")) {
                    /*Completo los vacios con Aleatorios. Util cuando se desea
                    especificar las incognitas sin lenar el resto de las celdas*/
                    model.setValueAt(String.valueOf(
                            AGenApp.calcularPrioridadAleatoria()), i, j);
                }
            }
        }
        model.completandoConRandom = false;
  }
     /**
      * desde el thear se detecta si el usuario hizo pausa y se llama a este
      * metodo para visualizar los valores relevantes en pantalla.
      */
     public static void setPauseValues(double convergencia, double aptitud,
             int numIteracion, double consistencia, byte[] individuoOp){
         System.err.println("Se hizo la pausa carajo");
         interfaz.getJlblCoherencia().setText(String.valueOf(consistencia));
         interfaz.getJlblConvergencia().setText(String.valueOf(convergencia));
         interfaz.getJlblMejorAptitud().setText(String.valueOf(aptitud));
         interfaz.getJlblIteracion().setText(String.valueOf(numIteracion));
         interfaz.getJlblIndividuoOptimo().setText(armarStringIndividuo(individuoOp));
         interfaz.getJbtCorrer().setSelected(false);
         interfaz.correrAction();
         AHPTableModel model = (AHPTableModel) interfaz.getJtbMultiCriterioModel();
         int elto = 0;
         for (int j = interfaz.eltosIndivPos.length - 1; j >= 0; j--) {
                    /*Importante tener en cuenta que la ubicacion debe cargarse al
                     * revez*/
                     double prio = prioridades[individuoOp[elto++]];
                     String strPrio = prio > 1? String.valueOf((int)prio)
                             : "1/" + String.valueOf(Math.round(1/prio));
                    model.setValueAt(strPrio
                            , interfaz.eltosIndivPos[j][0]
                            , interfaz.eltosIndivPos[j][1] + 1);
//                    model.setValueAt(prioridades[individuoOp[elto++]]
//                            , interfaz.eltosIndivPos[j][1]
//                            , interfaz.eltosIndivPos[j][0] + 1);
                }
         model.fireTableDataChanged();
     }
     public static void actualizarGraficoAptitudesXGen(int generacion){
         XYChart.agregarAPanel(interfaz.getPanelGraficosApXGen(),
                 ArmarDataSets.armarDataSetAptitudes(
                    generacion, historialAptitudes[generacion]),
                    "Aptitudes Generacion " + generacion, false,
                    "Individuos", "Aptitudes");
         int indexMejorAp = historialAptitudesOrdenadas[generacion][0];
         String msgMejora;
         if (generacion == 0) {
             msgMejora = "No hubo mejora.";
         }else if (indexMejorAp < correrThead.getOffsets()[0]){
             /*En esta generacion No hubo mejora Porque el mejor esta 
              en el segmento formado por selección*/
             msgMejora = "No hubo mejora.";
         } else if (indexMejorAp < correrThead.getOffsets()[1]){
             /*El mejor en el segmento Cruce*/
             msgMejora = "Sí, debido a Cruce.";
         } else {
             /*El mejor en el segmento Mutación*/
             msgMejora = "Sí, debido a Mutación.";
         }
         interfaz.getLblMejorAptitud().setText(msgMejora);

     }
}
